#include "raito_udp.h"


int printit(){
    return 100;
}

LinkMap *linkMap;
LinkMap *dataMap;
LinkList *fdListList;
LinkMap *publishBuffer;

int _size;
int _mtu = 200;
uint16_t _address;
void (*_data_cb)(uint16_t dst, Bytes *bytes, SeqFlag flag);
uint64_t (*_nextSeq)();
uint8_t _forwardFlag = DISABLE_FORWARD;

void freeDataKey(void *key){
	if(key != NULL){
		DataKey *dataKey = (DataKey*)key;
		free(dataKey);
	}
}

void freeUint16Key(void *key){
	if(key != NULL){
		uint16_t *dst = (uint16_t*)key;
		freeUint16(dst);
	}
}


void freeUint64Val(void *value){
	if(value != NULL){
		uint64_t *seq = (uint64_t*)value;
		freeUint64(seq);
	}
}

void freeForwardData(void *value){
	if(value != NULL){
		ForwardData *forwardData = (ForwardData*)value;
		free(forwardData);
	}
}

bool compareDataKey(void *key1, void *key2){
	DataKey *dataKey1 = (DataKey*)key1;
	DataKey *dataKey2 = (DataKey*)key2;
	return dataKey1->src == dataKey2->src && dataKey1->seq == dataKey2->seq;
}


bool compareUint16(void *key1, void *key2){
	uint16_t *dst1 = (uint16_t*)key1;
	uint16_t *dst2 = (uint16_t*)key2;
	return *dst1 == *dst2;
}


void dataBufferCreate(int size){
	_size = size;

	linkMap = createMap(&freeUint16Key, &freeUint64Val, &compareDataKey);
	dataMap = createMap(&freeDataKey, &freeBytes, &compareDataKey);
	fdListList = create(&freeDataKey);
}

void setDataMTU(int mtu){
	_mtu = mtu;
}



void myAddress(uint16_t address){
	_address = address;

}


void nextSeqHandler(uint64_t (*next_seq)()){
	_nextSeq = next_seq;
}

void enableForward(uint8_t seqFlag){
	_forwardFlag = seqFlag;
}

void dataHandler(void (*data_cb)(uint16_t dst, Bytes *bytes, SeqFlag flag)){
	_data_cb = data_cb;
}


void copyBytes(Bytes *bytes1, int src , Bytes *bytes2, int dst, int len){

	for(int i=0; i < len; i++){
		bytes2->data[i + dst] = bytes1->data[i + src];
	}

}

void copyUint8(uint8_t *bytes1, int src, uint8_t *bytes2, int dst, int len){

	for(int i=0; i < len; i++){

		bytes2[i + dst] = bytes1[i + src];

	}
}

int getDataLen(LinkList *dataBuffer){
	Iterator iter; iterator(&iter, dataBuffer);
	int size = 0;
	while(hasNext(&iter)){
		Bytes* bytes = (Bytes*)next(&iter);
		size += bytes->len - 16 - 4;
	}
	return size;
}

void updateSeq(uint16_t dst, uint64_t recSeq){
	uint16_t* dstKey = toUint16(dst);
	LinkList *link = getList(linkMap, dstKey);

	if(link != NULL){
		Iterator iter; iterator(&iter, link);
		while(hasNext(&iter)){
			uint64_t *prevSeq = (uint64_t *)next(&iter);
			if(*prevSeq == recSeq){
				return;
			}
			*prevSeq = recSeq;
		}
	}else{
		insert(linkMap, dstKey, toUint64(recSeq));
	}
}

LinkList *upsertBytes(uint16_t src, uint64_t firstSeq, uint8_t seg0, uint8_t *data, int len){


	DataKey dataKey;
	dataKey.src = src;
	dataKey.seq = firstSeq;

	LinkList *dataBuffer = getList(dataMap, &dataKey);

	if(dataBuffer == NULL){
		DataKey *newDataKey = (DataKey*) malloc(sizeof(DataKey));
		newDataKey->src = src;
		newDataKey->seq = firstSeq;

		Bytes *bytes = (Bytes*)malloc(sizeof(Bytes));
		bytes->data = data;
		bytes->len = len;
		insert(dataMap, newDataKey, bytes);
		dataBuffer = getList(dataMap,newDataKey);
	}

	Iterator iter; iterator(&iter, dataBuffer);

	bool update = false;
	while(hasNext(&iter)){
		Bytes* bytes = (Bytes*)next(&iter);
		if(bytes->data[14] == seg0){
			update = true;
			break;
		}
	}

	if(update == false){
		Bytes *bytes = (Bytes*) malloc(sizeof(Bytes));
		bytes->data = data;
		bytes->len = len;
		addLast(dataBuffer, bytes);
	}

	return dataBuffer;
}


void copySeqToData(uint8_t *data, uint64_t seq, int pos){
	data[pos ]  = (seq & 0xFF00000000000000) >> 56;
	data[pos + 1]  = (seq & 0xFF000000000000) >> 48;
	data[pos + 2]  = (seq & 0xFF0000000000) >> 40;
	data[pos + 3]  = (seq & 0xFF00000000) >> 32;
	data[pos + 4] = (seq & 0xFF000000) >> 24;
	data[pos + 5] = (seq & 0xFF0000) >> 16;
	data[pos + 6] = (seq & 0xFF00) >> 8;
	data[pos + 7] = (seq & 0xFF);
}



void setHeader(uint8_t *data,uint16_t src, uint16_t dst, uint64_t seq, uint8_t seg0, uint8_t segN){
	data[0] = 0x00;
	data[1] = 0xFF;
	data[2] = ((src & 0xFF00) >> 8) & 0xFF;
	data[3] = src & 0xFF;
	data[4] = ((dst & 0xFF00) >> 8) & 0xFF;
	data[5] = dst & 0xFF;
	copySeqToData(data, seq, 6);
	data[14] = seg0 & 0xFF;
	data[15] = segN & 0xFF;

}


Bytes *toSendAck(uint16_t src, uint16_t dst,uint64_t recvSeq, uint8_t seg0){

	Bytes *bytes = (Bytes*)malloc(sizeof(Bytes));
	uint8_t *data = (uint8_t *)malloc(30 *sizeof(uint8_t));
	bytes->data = data;
	bytes->len = 30;

	setHeader(data, src, dst, _nextSeq(), 0x00, 0x00);
	data[16] = 0x00;
	copySeqToData(data, recvSeq, 17);
	data[25] = seg0 & 0xFF;
	data[26] = 0x00;
	data[27] = 0x00;
	data[28] = 0x00;
	data[29] = 0x00;
	return bytes;
}




void encode(uint8_t *data, int len){

	uint8_t nid = data[0];
	int flag = nid & 0x7F;
	uint8_t ttl = data[1];


	if(flag == 0x80){
		// Decrypt data.
	}

	uint16_t src = ((data[2] & 0x00FF) << 8) | (data[3] & 0xFF);
	uint16_t dst = ((data[4] & 0x00FF) << 8) | (data[5] & 0xFF);
	uint64_t recSeq = ((data[6] & 0x00000000000000FF) << 56) |
			((data[7] & 0x000000000000FF) << 48) |
			((data[8] & 0x0000000000FF) << 40) |
			((data[9] & 0x00000000FF) << 32) |
			((data[10] & 0x000000FF) << 24) |
			((data[11] & 0x0000FF) << 16) |
			((data[12] & 0x00FF) << 8) |
			(data[13] & 0xFF);



	if(dst != _address){

		if(_forwardFlag == DISABLE_FORWARD){
			return;
		}


		Bytes bytes;
		bytes.data = data;
		bytes.len = len;


		bool ignore = false;
		Iterator iter; iterator(&iter, fdListList);
		while(hasNext(&iter)){
			ForwardData *fdData = (ForwardData*)next(&iter);
			if(fdData->src == src && fdData->ttl >= ttl && fdData->seq == recSeq){
				ignore = true;
				fdData->seq = recSeq;
				break;
			}
		}

		if(ignore == true){
			printf("ignore forward data.");
			return;
		}


		if(_forwardFlag & BL_FORWARD != 0){
			Bytes *totalBytes = (Bytes*) malloc(sizeof(Bytes));
			int dataLen = bytes.len - 16 - 4;
			totalBytes->data = (uint8_t*) malloc(dataLen * sizeof(uint8_t));
			totalBytes->len = dataLen;
			copyBytes(&bytes, 16 , totalBytes, 0, dataLen);
			_data_cb(dst, totalBytes , BL_FORWARD);
		}


		if(_forwardFlag & UDP_FORWARD != 0){
			Bytes *totalBytes = (Bytes*) malloc(sizeof(Bytes));
			data[1] = ttl - 1;
			totalBytes->data = data;
			totalBytes->len = len;
			_data_cb(dst, totalBytes, UDP_FORWARD);
		}

	}else{

		updateSeq(src, recSeq);

		uint8_t seg0 = data[14] & 0xFF;
		uint8_t segN = data[15] & 0xFF;

		uint64_t firstSeq = (recSeq - seg0);
//		printf("rec seq  %d seg0 %d segN %d  \n", firstSeq, seg0, segN);
		LinkList *dataBuffer = upsertBytes(src, firstSeq, seg0, data, len);
		if((dataBuffer->size - 1) == segN){
			int totalSize = getDataLen(dataBuffer);
//			printf("totalSize %d  \n", totalSize);
			Bytes *totalBytes = (Bytes*) malloc(sizeof(Bytes));
			totalBytes->data = (uint8_t *) malloc(totalSize * sizeof(uint8_t));
			totalBytes->len = totalSize;
//			printf("dataBuffer size %d \n", dataBuffer->size);
			int dstPos = 0;
			int maxSize = dataBuffer->size;
			for(int i=0; i< maxSize; i++){
				Iterator iter; iterator(&iter, dataBuffer);
				while(hasNext(&iter)){
					Bytes* bytes = (Bytes*)next(&iter);
//					printf("bytes seg0 %d i %d \n",bytes->data[14], i);
					if(bytes->data[14] == i){
						int len = (bytes->len - 16 - 4);
//						printf("seg size len %d \n", len);
						copyBytes(bytes, 16, totalBytes, dstPos, len);
						dstPos += len;
						delNode(&iter);
						break;
					}
				}
			}

			if(totalBytes->data[0] == 0x00){
				_data_cb(src, totalBytes, RECV_ACK_DATA);
			}else{
				_data_cb(src, totalBytes, COMPLETED);
			}

		}

		if(!((seg0 == 0x00 && segN == 0x00) && data[16] == 0x00)){
			Bytes *ackBytes = toSendAck(dst, src, recSeq, seg0);
						_data_cb(src, ackBytes, SEND_ACK_DATA);
		}

	}
}



LinkList *decode(uint8_t *data,  int len, uint16_t src, uint16_t dst){


	int segSize = len / _mtu;

	if(len % _mtu > 0){
		segSize += 1;
	}
	int segN = segSize - 1;

	LinkList *linkList = create(&freeBytes);
	int offset = 0;
	for (int i=0; i < segN; i++){

		int segDataSize = _mtu + 16 + 4;
		uint8_t *segData = (uint8_t*)malloc(segDataSize * sizeof(uint8_t));

		Bytes *segBytes = (Bytes*)malloc(sizeof(Bytes));
		segBytes->data = segData;
		segBytes->len = segDataSize;

		uint64_t seq = _nextSeq();

		setHeader(segData, src, dst, seq, i, segN);
		copyUint8(data, offset, segData, 16, _mtu);
		offset += _mtu;
		addLast(linkList, segBytes);

	}

	int dataSize = (len - offset);
	int lastSize = dataSize + 16 + 4;
	uint8_t *lastSegData = (uint8_t*)malloc(lastSize * sizeof(uint8_t));

	Bytes *lastSegBytes = (Bytes*)malloc(sizeof(Bytes));
	lastSegBytes->data = lastSegData;
	lastSegBytes->len = lastSize;

	uint64_t seq = _nextSeq();
	setHeader(lastSegData, src, dst, seq, segN, segN);
	copyUint8(data, offset, lastSegData, 16, dataSize);

	addLast(linkList, lastSegBytes);

	return linkList;
}

